/**
 * s_cpu.c
 * CPU source for TrayInfo.
 * CPU usage support added by Laszlo Ashin <kodest@gmail.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>

#include "trayinfo.h"
#include "utils.h"

static int limit;

typedef struct proc_s {
	int pid;
	long unsigned curtime, lasttimes;
	int accessed;
	struct proc_s *next;
} proc_t;

static proc_t *first;

static char *
evilproc_scan(void)
{
	static char evilproc[64];

	DIR *d;
	struct dirent *e;
	proc_t *pp, **ppp;
	proc_t *max = NULL;

	d = opendir("/proc");
	if (!d) return NULL;

	for (pp = first; pp; pp = pp->next)
		pp->accessed = 0;

	while ((e = readdir(d))) {
		int pid;
		char buf[256];
		int n;
		FILE *fp;
		char *name, *p;
		char status;
		long unsigned utime, stime;

		pid = atoi(e->d_name);
		if (pid < 1) continue;

		n = snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
		if (n < 1 || n >= sizeof(buf))
			continue;

		fp = fopen(buf, "rt");
		if (!fp) continue;
		if (fgets(buf, sizeof(buf), fp) != buf) goto fclo;

		if (atoi(buf) != pid) continue;

		name = strchr(buf, '(');
		if (!name) goto fclo;
		++name;

		p = strchr(name, ')');
		*p++ = '\0';

		n = sscanf(p, " %c %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %lu %lu", &status, &utime, &stime);
		if (n != 3) continue;

		for (pp = first; pp && pp->pid != pid; pp = pp->next);
		if (!pp) {
			pp = NEW(proc_t);
			if (!pp) continue;
			pp->pid = pid;
			pp->lasttimes = 0;
			pp->next = first;
			first = pp;
		}
		pp->accessed = 1;
		pp->curtime = utime + stime - pp->lasttimes;
		if (!max || pp->curtime > max->curtime) {
			max = pp;
			strncpy(evilproc, name, sizeof(evilproc));
			evilproc[sizeof(evilproc) - 1] = '\0';
		}

		pp->lasttimes = utime + stime;

	fclo:	fclose(fp);
	}
	closedir(d);

	pp = first;
	ppp = &first;
	while (pp)
		if (!pp->accessed) {
			proc_t *q = pp;

			pp = q->next;
			*ppp = pp;
			free(q);
		} else {
			ppp = &(pp->next);
			pp = pp->next;
		}

	return evilproc;
}

static int
cpu_write(struct timeval *now, int fd)
{
	static unsigned used, total, old_used, old_total;
	unsigned ld[4];
	int i;
	FILE *f;
	const char *evilproc;
	
	f = fopen("/proc/stat", "r");
	if (!f) return 0;
	i = fscanf(f, "%*s %u %u %u %u", ld, ld + 1, ld + 2, ld + 3);
	fclose(f);
	if (i != 4) return 0;
	used = ld[0] + ld[1] + ld[2];
	total = used + ld[3];
	if (total != old_total)
		i = (used - old_used) * 99 / (total - old_total);
	else
		i = 0;

	old_used = used;
	old_total = total;
	printf("%2d%%", i);

	evilproc = evilproc_scan();
	if (evilproc && (i >= limit)) {
		putchar('-');
		fwrite(evilproc, strlen(evilproc), 1, stdout);
	}

	return 1;
}

static void
cpu_init(void)
{
	limit = 50;
	first = NULL;
}

static void
cpu_done(void)
{
	while (first) {
		proc_t *p = first;
		first = p->next;
		free(p);
	}
}

static int
cpu_cfg_limit(const char *s)
{
	return cfgstr2int(s, &limit);
}

void
regsrc_cpu(src_t *src)
{
	src->name = "cpu";
	src->init = cpu_init;
	src->done = cpu_done;
	src->write = cpu_write;

	cfg_add_handler("cpu-limit", cpu_cfg_limit);
}
